/* 
 *	Copyright (C) 2003-2006 Gabest
 *	http://www.gabest.org
 *
 *  This Program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *   
 *  This Program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *  http://www.gnu.org/copyleft/gpl.html
 *
 */

#include "stdafx.h"
#include "SubtitleInputPin.h"
#include "VobSubFile.h"
#include "RTS.h"
#include "SSF.h"

#include <initguid.h>
#include <moreuuids.h>

// our first format id
#define __GAB1__ "GAB1"

// our tags for __GAB1__ (ushort) + size (ushort)

// "lang" + '0'
#define __GAB1_LANGUAGE__ 0
// (int)start+(int)stop+(char*)line+'0'
#define __GAB1_ENTRY__ 1
// L"lang" + '0'
#define __GAB1_LANGUAGE_UNICODE__ 2
// (int)start+(int)stop+(WCHAR*)line+'0'
#define __GAB1_ENTRY_UNICODE__ 3

// same as __GAB1__, but the size is (uint) and only __GAB1_LANGUAGE_UNICODE__ is valid
#define __GAB2__ "GAB2"

// (BYTE*)
#define __GAB1_RAWTEXTSUBTITLE__ 4

CSubtitleInputPin::CSubtitleInputPin(CBaseFilter* pFilter, CCritSec* pLock, CCritSec* pSubLock, HRESULT* phr)
	: CBaseInputPin(NAME("CSubtitleInputPin"), pFilter, pLock, phr, L"Input")
	, m_pSubLock(pSubLock)
{
	m_bCanReconnectWhenActive = TRUE;
}

HRESULT CSubtitleInputPin::CheckMediaType(const CMediaType* pmt)
{
	return pmt->majortype == MEDIATYPE_Text && (pmt->subtype == MEDIASUBTYPE_NULL || pmt->subtype == FOURCCMap((DWORD)0))
		|| pmt->majortype == MEDIATYPE_Subtitle && pmt->subtype == MEDIASUBTYPE_UTF8
		|| pmt->majortype == MEDIATYPE_Subtitle && (pmt->subtype == MEDIASUBTYPE_SSA || pmt->subtype == MEDIASUBTYPE_ASS || pmt->subtype == MEDIASUBTYPE_ASS2)
		|| pmt->majortype == MEDIATYPE_Subtitle && pmt->subtype == MEDIASUBTYPE_SSF
		|| pmt->majortype == MEDIATYPE_Subtitle && (pmt->subtype == MEDIASUBTYPE_VOBSUB)
		? S_OK 
		: E_FAIL;
}

HRESULT CSubtitleInputPin::CompleteConnect(IPin* pReceivePin)
{
	if(m_mt.majortype == MEDIATYPE_Text)
	{
		if(!(m_pSubStream = new CRenderedTextSubtitle(m_pSubLock))) return E_FAIL;
		CRenderedTextSubtitle* pRTS = (CRenderedTextSubtitle*)(ISubStream*)m_pSubStream;
		pRTS->m_name = CString(GetPinName(pReceivePin)) + _T(" (embeded)");
		pRTS->m_dstScreenSize = CSize(384, 288);
		pRTS->CreateDefaultStyle(DEFAULT_CHARSET);
	}
	else if(m_mt.majortype == MEDIATYPE_Subtitle)
	{
		SUBTITLEINFO* psi = (SUBTITLEINFO*)m_mt.pbFormat;
		DWORD dwOffset = psi->dwOffset;

		CString name = ISO6392ToLanguage(psi->IsoLang);
		LCID	lcid = ISO6392ToLcid(psi->IsoLang);
		if(name.IsEmpty()) name = _T("English");
		if(wcslen(psi->TrackName) > 0) name += _T(" (") + CString(psi->TrackName) + _T(")");

		if(m_mt.subtype == MEDIASUBTYPE_UTF8 
		/*|| m_mt.subtype == MEDIASUBTYPE_USF*/
		|| m_mt.subtype == MEDIASUBTYPE_SSA 
		|| m_mt.subtype == MEDIASUBTYPE_ASS 
		|| m_mt.subtype == MEDIASUBTYPE_ASS2)
		{
			if(!(m_pSubStream = new CRenderedTextSubtitle(m_pSubLock))) return E_FAIL;
			CRenderedTextSubtitle* pRTS = (CRenderedTextSubtitle*)(ISubStream*)m_pSubStream;
			pRTS->m_name = name;
			pRTS->m_lcid = lcid;
			pRTS->m_dstScreenSize = CSize(384, 288);
			pRTS->CreateDefaultStyle(DEFAULT_CHARSET);

			if(dwOffset > 0 && m_mt.cbFormat - dwOffset > 0)
			{
				CMediaType mt = m_mt;
				if(mt.pbFormat[dwOffset+0] != 0xef
				&& mt.pbFormat[dwOffset+1] != 0xbb
				&& mt.pbFormat[dwOffset+2] != 0xfb)
				{
					dwOffset -= 3;
					mt.pbFormat[dwOffset+0] = 0xef;
					mt.pbFormat[dwOffset+1] = 0xbb;
					mt.pbFormat[dwOffset+2] = 0xbf;
				}

				pRTS->Open(mt.pbFormat + dwOffset, mt.cbFormat - dwOffset, DEFAULT_CHARSET, pRTS->m_name);
			}

		}
		else if(m_mt.subtype == MEDIASUBTYPE_SSF)
		{
			if(!(m_pSubStream = new ssf::CRenderer(m_pSubLock))) return E_FAIL;
			ssf::CRenderer* pSSF = (ssf::CRenderer*)(ISubStream*)m_pSubStream;
			pSSF->Open(ssf::MemoryInputStream(m_mt.pbFormat + dwOffset, m_mt.cbFormat - dwOffset, false, false), name);
		}
		else if(m_mt.subtype == MEDIASUBTYPE_VOBSUB)
		{
			if(!(m_pSubStream = new CVobSubStream(m_pSubLock))) return E_FAIL;
			CVobSubStream* pVSS = (CVobSubStream*)(ISubStream*)m_pSubStream;
			pVSS->Open(name, m_mt.pbFormat + dwOffset, m_mt.cbFormat - dwOffset);
		}
	}

	AddSubStream(m_pSubStream);

    return __super::CompleteConnect(pReceivePin);
}

HRESULT CSubtitleInputPin::BreakConnect()
{
	RemoveSubStream(m_pSubStream);
	m_pSubStream = NULL;

	ASSERT(IsStopped());

    return __super::BreakConnect();
}

STDMETHODIMP CSubtitleInputPin::ReceiveConnection(IPin* pConnector, const AM_MEDIA_TYPE* pmt)
{
	if(m_Connected)
	{
		RemoveSubStream(m_pSubStream);
		m_pSubStream = NULL;

        m_Connected->Release();
        m_Connected = NULL;
	}

	return __super::ReceiveConnection(pConnector, pmt);
}

STDMETHODIMP CSubtitleInputPin::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
{
	CAutoLock cAutoLock(&m_csReceive);

	if(m_mt.majortype == MEDIATYPE_Text
	|| m_mt.majortype == MEDIATYPE_Subtitle 
		&& (m_mt.subtype == MEDIASUBTYPE_UTF8 
		/*|| m_mt.subtype == MEDIASUBTYPE_USF*/
		|| m_mt.subtype == MEDIASUBTYPE_SSA 
		|| m_mt.subtype == MEDIASUBTYPE_ASS 
		|| m_mt.subtype == MEDIASUBTYPE_ASS2))
	{
		CAutoLock cAutoLock(m_pSubLock);
		CRenderedTextSubtitle* pRTS = (CRenderedTextSubtitle*)(ISubStream*)m_pSubStream;
		pRTS->RemoveAll();
		pRTS->CreateSegments();
	}
	else if(m_mt.majortype == MEDIATYPE_Subtitle && m_mt.subtype == MEDIASUBTYPE_SSF)
	{
		CAutoLock cAutoLock(m_pSubLock);
		ssf::CRenderer* pSSF = (ssf::CRenderer*)(ISubStream*)m_pSubStream;
		// LAME, implement RemoveSubtitles
		DWORD dwOffset = ((SUBTITLEINFO*)m_mt.pbFormat)->dwOffset;
		pSSF->Open(ssf::MemoryInputStream(m_mt.pbFormat + dwOffset, m_mt.cbFormat - dwOffset, false, false), _T(""));
		// pSSF->RemoveSubtitles();
	}
	else if(m_mt.majortype == MEDIATYPE_Subtitle && (m_mt.subtype == MEDIASUBTYPE_VOBSUB))
	{
		CAutoLock cAutoLock(m_pSubLock);
		CVobSubStream* pVSS = (CVobSubStream*)(ISubStream*)m_pSubStream;
		pVSS->RemoveAll();
	}

	return __super::NewSegment(tStart, tStop, dRate);
}

STDMETHODIMP CSubtitleInputPin::Receive(IMediaSample* pSample)
{
	HRESULT hr;

	hr = __super::Receive(pSample);
    if(FAILED(hr)) return hr;

	CAutoLock cAutoLock(&m_csReceive);

	REFERENCE_TIME tStart, tStop;
    pSample->GetTime(&tStart, &tStop);
	tStart += m_tStart; 
	tStop += m_tStart;

	BYTE* pData = NULL;
    hr = pSample->GetPointer(&pData);
    if(FAILED(hr) || pData == NULL) return hr;

	int len = pSample->GetActualDataLength();

	bool fInvalidate = false;

	if(m_mt.majortype == MEDIATYPE_Text)
	{
		CAutoLock cAutoLock(m_pSubLock);
		CRenderedTextSubtitle* pRTS = (CRenderedTextSubtitle*)(ISubStream*)m_pSubStream;

		if(!strncmp((char*)pData, __GAB1__, strlen(__GAB1__)))
		{
			char* ptr = (char*)&pData[strlen(__GAB1__)+1];
			char* end = (char*)&pData[len];

			while(ptr < end)
			{
				WORD tag = *((WORD*)(ptr)); ptr += 2;
				WORD size = *((WORD*)(ptr)); ptr += 2;

				if(tag == __GAB1_LANGUAGE__)
				{
					pRTS->m_name = CString(ptr);
				}
				else if(tag == __GAB1_ENTRY__)
				{
					pRTS->Add(AToW(&ptr[8]), false, *(int*)ptr, *(int*)(ptr+4));
					fInvalidate = true;
				}
				else if(tag == __GAB1_LANGUAGE_UNICODE__)
				{
					pRTS->m_name = (WCHAR*)ptr;
				}
				else if(tag == __GAB1_ENTRY_UNICODE__)
				{
					pRTS->Add((WCHAR*)(ptr+8), true, *(int*)ptr, *(int*)(ptr+4));
					fInvalidate = true;
				}

				ptr += size;
			}
		}
		else if(!strncmp((char*)pData, __GAB2__, strlen(__GAB2__)))
		{
			char* ptr = (char*)&pData[strlen(__GAB2__)+1];
			char* end = (char*)&pData[len];

			while(ptr < end)
			{
				WORD tag = *((WORD*)(ptr)); ptr += 2;
				DWORD size = *((DWORD*)(ptr)); ptr += 4;

				if(tag == __GAB1_LANGUAGE_UNICODE__)
				{
					pRTS->m_name = (WCHAR*)ptr;
				}
				else if(tag == __GAB1_RAWTEXTSUBTITLE__)
				{
					pRTS->Open((BYTE*)ptr, size, DEFAULT_CHARSET, pRTS->m_name);
					fInvalidate = true;
				}

				ptr += size;
			}
		}
		else if(pData != 0 && len > 1 && *pData != 0)
		{
			CStringA str((char*)pData, len);

			str.Replace("\r\n", "\n");
			str.Trim();

			if(!str.IsEmpty())
			{
				pRTS->Add(AToW(str), false, (int)(tStart / 10000), (int)(tStop / 10000));
				fInvalidate = true;
			}
		}
	}
	else if(m_mt.majortype == MEDIATYPE_Subtitle)
	{
		CAutoLock cAutoLock(m_pSubLock);

		if(m_mt.subtype == MEDIASUBTYPE_UTF8)
		{
			CRenderedTextSubtitle* pRTS = (CRenderedTextSubtitle*)(ISubStream*)m_pSubStream;

			CStringW str = UTF8To16(CStringA((LPCSTR)pData, len)).Trim();
			if(!str.IsEmpty())
			{
				pRTS->Add(str, true, (int)(tStart / 10000), (int)(tStop / 10000));
				fInvalidate = true;
			}
		}
		else if(m_mt.subtype == MEDIASUBTYPE_SSA || m_mt.subtype == MEDIASUBTYPE_ASS || m_mt.subtype == MEDIASUBTYPE_ASS2)
		{
			CRenderedTextSubtitle* pRTS = (CRenderedTextSubtitle*)(ISubStream*)m_pSubStream;

			CStringW str = UTF8To16(CStringA((LPCSTR)pData, len)).Trim();
			if(!str.IsEmpty())
			{
				STSEntry stse;

				int fields = m_mt.subtype == MEDIASUBTYPE_ASS2 ? 10 : 9;

				CAtlList<CStringW> sl;
				Explode(str, sl, ',', fields);
				if(sl.GetCount() == fields)
				{
					stse.readorder = wcstol(sl.RemoveHead(), NULL, 10);
					stse.layer = wcstol(sl.RemoveHead(), NULL, 10);
					stse.style = sl.RemoveHead();
					stse.actor = sl.RemoveHead();
					stse.marginRect.left = wcstol(sl.RemoveHead(), NULL, 10);
					stse.marginRect.right = wcstol(sl.RemoveHead(), NULL, 10);
					stse.marginRect.top = stse.marginRect.bottom = wcstol(sl.RemoveHead(), NULL, 10);
					if(fields == 10) stse.marginRect.bottom = wcstol(sl.RemoveHead(), NULL, 10);
					stse.effect = sl.RemoveHead();
					stse.str = sl.RemoveHead();
				}

				if(!stse.str.IsEmpty())
				{
					pRTS->Add(stse.str, true, (int)(tStart / 10000), (int)(tStop / 10000), 
						stse.style, stse.actor, stse.effect, stse.marginRect, stse.layer, stse.readorder);
					fInvalidate = true;
				}
			}
		}
		else if(m_mt.subtype == MEDIASUBTYPE_SSF)
		{
			ssf::CRenderer* pSSF = (ssf::CRenderer*)(ISubStream*)m_pSubStream;

			CStringW str = UTF8To16(CStringA((LPCSTR)pData, len)).Trim();
			if(!str.IsEmpty())
			{
				pSSF->Append(tStart, tStop, str);
				fInvalidate = true;
			}
		}
		else if(m_mt.subtype == MEDIASUBTYPE_VOBSUB)
		{
			CVobSubStream* pVSS = (CVobSubStream*)(ISubStream*)m_pSubStream;
			pVSS->Add(tStart, tStop, pData, len);
		}
	}

	if(fInvalidate)
	{
		TRACE(_T("InvalidateSubtitle(%I64d, ..)\n"), tStart);
		// IMPORTANT: m_pSubLock must not be locked when calling this
		InvalidateSubtitle(tStart, m_pSubStream);
	}

	hr = S_OK;

    return hr;
}


